/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2011, Salzburg Research. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - Neither the name of the KiWi Project nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Contributor(s):
 *  sschaffe
 *
 * File Info:
 *   User: sschaffe
 *   Date: 2011/02/18
 *   Time: 15:54
 *
 * Project: KiWi2, kiwi-core module
 */

package kiwi.core.osgi;

import kiwi.core.api.config.ConfigurationService;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.FrameworkFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.servlet.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * see http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.html
 * <p/>
 * User: sschaffe
 */
public class FelixInitialiser implements Filter {

    private static Logger logger = LoggerFactory.getLogger(FelixInitialiser.class);

    private static Felix felixFramework;

    private static KiWiBundleActivator kiWiBundleActivator;

    @Inject
    private ConfigurationService configurationService;

    /**
     * * Notification that the web application initialization
     * * process is starting.
     * * All ServletContextListeners are notified of context
     * * initialization before any filter or servlet in the web
     * * application is initialized.
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("Starting KiWi OSGi Services ...");

        Map config = new HashMap();

        kiWiBundleActivator = new KiWiBundleActivator();
        config.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, Collections.singletonList(kiWiBundleActivator));
        config.put(FelixConstants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "kiwi.core;version=1.99.1,org.slf4j;version=1.6.1");
        config.put(FelixConstants.FRAMEWORK_BUNDLE_PARENT, FelixConstants.FRAMEWORK_BUNDLE_PARENT_APP);
        config.put(FelixConstants.FRAMEWORK_STORAGE_CLEAN, FelixConstants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
        config.put("org.osgi.framework.storage", configurationService.getWorkDir()+File.separator+"felix-cache");


        try {
            // initialise and start Felix framework
            felixFramework = new Felix(config);
            felixFramework.start();


//            // check for all kiwi modules whether they are considered to be osgi bundles; a kiwi module always has a
//            // kiwi-module.properties file
//            List<URL> classPathEntries = new LinkedList<URL>();
//
//            Enumeration<URL> modulePropertiesEnum = this.getClass().getClassLoader().getResources("kiwi-module.properties");
//            while(modulePropertiesEnum.hasMoreElements()) {
//                URL url = modulePropertiesEnum.nextElement();
//
//                JarURLConnection conn = (JarURLConnection)url.openConnection();
//                classPathEntries.add(conn.getJarFileURL());
//            }

            // now look for all plugins that are configured in kiwi.plugins property and install them either from the
            // plugin directory or from the system path
            File pluginDir = new File(configurationService.getPluginDirectory());

            for(File plugin : pluginDir.listFiles()) {
                try {
                    if(plugin.exists() && plugin.canRead()) {
                        Bundle b = felixFramework.getBundleContext().installBundle(plugin.toURI().toString());
                        if(configurationService.getListConfiguration("kiwi.plugins").contains(plugin.getName())) {
                            b.start();
                            logger.info("OSGi bundle {} version {} loaded and started", b.getSymbolicName(), b.getVersion());
                        } else {
                            logger.info("OSGi bundle {} version {} loaded but not started since it is disabled",b.getSymbolicName(), b.getVersion());
                        }
//                    } else {
//                        for(URL cpe : classPathEntries) {
//                            if(cpe.getFile().endsWith(bundle)) {
//                                Bundle b = felixFramework.getBundleContext().installBundle(cpe.toString());
//                                logger.info("loaded OSGi bundle from system path: {} version {}",b.getSymbolicName(), b.getVersion());
//                                b.start();
//                                break;
//                            }
//                        }
                    }

                 } catch(Exception ex) {
                    logger.error("error installing plugin {}",plugin.getName());
                    logger.error("exception",ex);
                }
            }

        } catch (Exception ex) {
            logger.error("could not create OSGi framework:{}",ex);
        }
    }

    /**
     * * Notification that the servlet context is about to be shut down.
     * * All servlets and filters have been destroy()ed before any
     * * ServletContextListeners are notified of context
     * * destruction.
     */
     @Override
     public void destroy() {
        // Shut down the felix framework when stopping the
        // host application.
         try {
            felixFramework.stop();
            felixFramework.waitForStop(0);
        } catch (InterruptedException e) {
             logger.error("interruped while stopping OSGi framework", e);
        } catch (BundleException e) {
             logger.error("bundle exception while stopping OSGi framework",e);
         }
    }


    /**
     * The <code>doFilter</code> method of the Filter is called by the container
     * each time a request/response pair is passed through the chain due
     * to a client request for a resource at the end of the chain. The FilterChain passed in to this
     * method allows the Filter to pass on the request and response to the next entity in the
     * chain.<p>
     * A typical implementation of this method would follow the following pattern:- <br>
     * 1. Examine the request<br>
     * 2. Optionally wrap the request object with a custom implementation to
     * filter content or headers for input filtering <br>
     * 3. Optionally wrap the response object with a custom implementation to
     * filter content or headers for output filtering <br>
     * 4. a) <strong>Either</strong> invoke the next entity in the chain using the FilterChain object (<code>chain.doFilter()</code>), <br>
     * * 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the request processing<br>
     * * 5. Directly set headers on the response after invocation of the next entity in the filter chain.
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request,response);
    }

    private static FrameworkFactory getFrameworkFactory() throws Exception
    {
        URL url = FelixInitialiser.class.getClassLoader().getResource(
            "META-INF/services/org.osgi.framework.launch.FrameworkFactory");
        if (url != null)
        {
            BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
            try
            {
                for (String s = br.readLine(); s != null; s = br.readLine())
                {
                    s = s.trim();
                    // Try to load first non-empty, non-commented line.
                    if ((s.length() > 0) && (s.charAt(0) != '#'))
                    {
                        return (FrameworkFactory) Class.forName(s).newInstance();
                    }
                }
            }
            finally
            {
                if (br != null) br.close();
            }
        }

        throw new Exception("Could not find framework factory.");
    }

}
